Play the 8 puzzle on-line here.
Let's discuss how to implement the 8 puzzle in python.
How do you want to represent the state of the 8 puzzle? Say the state is
-------------
| 1 | 2 | 3 |
------------
| 4 | | 5 |
------------
| 6 | 7 | 8 |
-------------
You could use a list
state = [1, 2, 3, 4, 0, 5, 6, 7, 8]
state
[1, 2, 3, 4, 0, 5, 6, 7, 8]
with 0 representing the empty cell. You could represent it as a numpy array.
import numpy as np
state = np.array([[1, 2, 3], [4, 0, 5], [6, 7, 8]])
state
array([[1, 2, 3], [4, 0, 5], [6, 7, 8]])
This way you index into a cell using
state[1, 2]
5
for the second row and third column.
I found the simple list a little easier to work with. Then you can
write a print_state_8p
function to show it.
In [9]: print_state_8p(state)
1 2 3
4 - 5
6 7 8
Another useful function is one that finds the blank in a given state.
In [18]: find_blank_8p(state)
Out[18]: (1, 1)
In [19]: find_blank_8p([1,2,3, 4,7,5, 6,0,8])
Out[19]: (2, 1)
Other useful functions include ones that convert between an index into the list state and a row and column pair.
One bit of trickiness in the iterative deepening algorithm, repeated here
from last time, is that sometimes a list of states is returned as the
solution path, and other times the string 'cutoff'
or 'failure'
is returned.
def depth_limited_search(state, goal_state, actions_f, take_action_f, depth_limit):
# If we have reached the goal, exit, returning an empty solution path.
If state == goal_state, then
return []
# If we have reached the depth limit, return the string 'cutoff'.
If depth_limit is 0, then
Return the string 'cutoff' to signal that the depth limit was reached
cutoff_occurred = False
# For each possible action from state ...
For each action in actions_f(state):
# Apply the action to the current state to get a next state, named child_state
child_state = take_action_f(state, action)
# Recursively call this function to continue the search starting from the child_state.
# Decrease by one the depth_limit for this search.
result = depth_limited_search(child_state, goal_state, actions_f, take_action_f, depth_limit - 1)
# If result was 'cufoff', just note that this happened.
If result is 'cutoff', then
cutoff_occurred = True
# If result was not 'failure', search succeeded so add childState to front of solution path and
# return that path.
else if result is not 'failure' then
Add child_state to front of partial solution path, in result, returned by depth_limited_search
return result
# We reach here only if cutoff or failure occurred. Return whichever occurred.
If cutoff_occurred, then
return 'cutoff'
else
return 'failure'
def iterative_deepening_search(start_state, goal_state, actions_f, take_action_f, max_depth):
# Conduct multiple searches, starting with smallest depth, then increasing it by 1 each time.
for depth in range(max_depth):
# Conduct search from startState
result = depth_limited_search(start_state, goal_state, actions_f, take_action_f, depth)
# If result was failure, return 'failure'.
if result is 'failure':
return 'failure'
# Otherwise, if result was not cutoff, it succeeded, so add start_state to solution path and return it.
if result is not 'cutoff', then
Add start_state to front of solution path, in result, returned by depth_limited_search
return result
# If we reach here, no solution found within the max_depth limit.
return 'cutoff'
Remember, for the 8 puzzle all actions are not available from all states. The state
-------------
| | 2 | 3 |
------------
| 1 | 4 | 5 |
------------
| 6 | 7 | 8 |
-------------
only has two possible actions, 'down' and 'right'. It makes the most
sense to implement this restriction in the actions_f
function, so
take_action_f
can assume only valid actions are given to it.
As implemented for this assignment, our depth-limited search generates
a list of all valid actions from a state, stores them, then starts a
for
loop to try each one. At any point in the depth-first search,
all siblings of states being explored are stored in the local
variables of each recursive call.
Remember that the "backtracking" version of depth-first search is one in which all sibling actions are not stored, but generated as needed.
Sounds like a complicated implementation. Python generators to the rescue! This is a bit advanced and the solution to Assignment 2 does not need generators, but, be curious!
Here is a simplified version of actions_f
, without the checks for
valid actions.
def actions_f(state):
actions = []
actions.append('left')
actions.append('right')
actions.append('up')
actions.append('down')
return actions
It just returns the actions.
In [31]: actions_f(state)
Out[31]: ['left', 'right', 'up', 'down']
state = np.array([[1, 2, 3], [4, 0, 5], [6, 7, 8]])
state
array([[1, 2, 3], [4, 0, 5], [6, 7, 8]])
acts = actions_f(state)
acts
['left', 'right', 'up', 'down']
The function actions_f
can be converted to one that returns a
generator by using the yield
statement.
def actions_f(state):
yield 'left'
yield 'right'
yield 'up'
yield 'down'
Sheesh. That's even simpler than the original. It's use must be more complicated. And it is, but just a bit.
acts = actions_f(state)
acts
<generator object actions_f at 0x7fa9643e26d0>
next(acts)
'left'
next(acts)
'right'
next(acts)
'up'
next(acts)
'down'
next(acts)
--------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-26-588cd4d23904> in <module> ----> 1 next(acts) StopIteration:
That last one raised a StopIteration
exception. The generator is
often used in a for
loop that stops correctly.
for a in actions_f(state):
print(a)
left right up down
This looks exactly like the for
loop when actions_f
actually
returns the whole list!
See the site Python Conquers the Universe for a brief introduction to using the pdb
module.
And don't forget good old print
statements.
debug = True
.
.
.
if debug:
print('Just loaded data into list named nums whose length is', len(nums))
ipython
can be set to automatically start pdb
when an error is encountered. Many other settings are available. See IPython Tip Sheet.
jupyter
startup settings are discussed here